/*!
 * better-normal-scroll v1.15.0
 * (c) 2016-2019 ustbhuangyi
 * Released under the MIT License.
 */
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
      (global.BScroll = factory());
}(this, (function () {
  'use strict';

  // As of V8 6.6, depending on the size of the array, this is anywhere
  // between 1.5-10x faster than the two-arg version of Array#splice()
  function spliceOne(list, index) {
    for (; index + 1 < list.length; index++) {
      list[index] = list[index + 1];
    }

    list.pop();
  }

  var slicedToArray = function () {
    function sliceIterator(arr, i) {
      var _arr = [];
      var _n = true;
      var _d = false;
      var _e = undefined;

      try {
        for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
          _arr.push(_s.value);

          if (i && _arr.length === i) break;
        }
      } catch (err) {
        _d = true;
        _e = err;
      } finally {
        try {
          if (!_n && _i["return"]) _i["return"]();
        } finally {
          if (_d) throw _e;
        }
      }

      return _arr;
    }

    return function (arr, i) {
      if (Array.isArray(arr)) {
        return arr;
      } else if (Symbol.iterator in Object(arr)) {
        return sliceIterator(arr, i);
      } else {
        throw new TypeError("Invalid attempt to destructure non-iterable instance");
      }
    };
  }();













  var toConsumableArray = function (arr) {
    if (Array.isArray(arr)) {
      for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];

      return arr2;
    } else {
      return Array.from(arr);
    }
  };

  function eventMixin(BScroll) {
    BScroll.prototype.on = function (type, fn) {
      var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this;

      if (!this._events[type]) {
        this._events[type] = [];
      }

      this._events[type].push([fn, context]);
    };

    BScroll.prototype.once = function (type, fn) {
      var context = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this;

      function magic() {
        this.off(type, magic);

        fn.apply(context, arguments);
      }
      // To expose the corresponding function method in order to execute the off method
      magic.fn = fn;

      this.on(type, magic);
    };

    BScroll.prototype.off = function (type, fn) {
      var _events = this._events[type];
      if (!_events) {
        return;
      }

      var count = _events.length;
      while (count--) {
        if (_events[count][0] === fn || _events[count][0] && _events[count][0].fn === fn) {
          spliceOne(_events, count);
        }
      }
    };

    BScroll.prototype.trigger = function (type) {
      var events = this._events[type];
      if (!events) {
        return;
      }

      var len = events.length;
      var eventsCopy = [].concat(toConsumableArray(events));
      for (var i = 0; i < len; i++) {
        var event = eventsCopy[i];

        var _event = slicedToArray(event, 2),
          fn = _event[0],
          context = _event[1];

        if (fn) {
          fn.apply(context, [].slice.call(arguments, 1));
        }
      }
    };
  }

  // ssr support
  var inBrowser = typeof window !== 'undefined';
  var ua = inBrowser && navigator.userAgent.toLowerCase();
  var isWeChatDevTools = ua && /wechatdevtools/.test(ua);
  var isAndroid = ua && ua.indexOf('android') > 0;

  function getNow() {
    return window.performance && window.performance.now ? window.performance.now() + window.performance.timing.navigationStart : +new Date();
  }

  function extend(target) {
    for (var _len = arguments.length, rest = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
      rest[_key - 1] = arguments[_key];
    }

    for (var i = 0; i < rest.length; i++) {
      var source = rest[i];
      for (var key in source) {
        target[key] = source[key];
      }
    }
    return target;
  }

  function isUndef(v) {
    return v === undefined || v === null;
  }

  function getDistance(x, y) {
    return Math.sqrt(x * x + y * y);
  }

  var elementStyle = inBrowser && document.createElement('div').style;

  var vendor = function () {
    if (!inBrowser) {
      return false;
    }
    // first pick up standard to fix #743
    var transformNames = {
      standard: 'transform',
      webkit: 'webkitTransform',
      Moz: 'MozTransform',
      O: 'OTransform',
      ms: 'msTransform'
    };

    for (var key in transformNames) {
      if (elementStyle[transformNames[key]] !== undefined) {
        return key;
      }
    }

    return false;
  }();

  function prefixStyle(style) {
    if (vendor === false) {
      return false;
    }

    if (vendor === 'standard') {
      if (style === 'transitionEnd') {
        return 'transitionend';
      }
      return style;
    }

    return vendor + style.charAt(0).toUpperCase() + style.substr(1);
  }

  function addEvent(el, type, fn, capture) {
    el.addEventListener(type, fn, { passive: false, capture: !!capture });
  }

  function removeEvent(el, type, fn, capture) {
    el.removeEventListener(type, fn, { passive: false, capture: !!capture });
  }

  function offset(el) {
    var left = 0;
    var top = 0;

    while (el) {
      left -= el.offsetLeft;
      top -= el.offsetTop;
      el = el.offsetParent;
    }

    return {
      left: left,
      top: top
    };
  }

  function offsetToBody(el) {
    var rect = el.getBoundingClientRect();

    return {
      left: -(rect.left + window.pageXOffset),
      top: -(rect.top + window.pageYOffset)
    };
  }

  var cssVendor = vendor && vendor !== 'standard' ? '-' + vendor.toLowerCase() + '-' : '';

  var transform = prefixStyle('transform');
  var transition = prefixStyle('transition');

  var hasPerspective = inBrowser && prefixStyle('perspective') in elementStyle;
  // fix issue #361
  var hasTouch = inBrowser && ('ontouchstart' in window || isWeChatDevTools);
  var hasTransform = transform !== false;
  var hasTransition = inBrowser && transition in elementStyle;

  var style = {
    transform: transform,
    transition: transition,
    transitionTimingFunction: prefixStyle('transitionTimingFunction'),
    transitionDuration: prefixStyle('transitionDuration'),
    transitionDelay: prefixStyle('transitionDelay'),
    transformOrigin: prefixStyle('transformOrigin'),
    transitionEnd: prefixStyle('transitionEnd')
  };

  var TOUCH_EVENT = 1;
  var MOUSE_EVENT = 2;

  var eventType = {
    touchstart: TOUCH_EVENT,
    touchmove: TOUCH_EVENT,
    touchend: TOUCH_EVENT,

    mousedown: MOUSE_EVENT,
    mousemove: MOUSE_EVENT,
    mouseup: MOUSE_EVENT
  };

  function getRect(el) {
    if (el instanceof window.SVGElement) {
      var rect = el.getBoundingClientRect();
      return {
        top: rect.top,
        left: rect.left,
        width: rect.width,
        height: rect.height
      };
    } else {
      return {
        top: el.offsetTop,
        left: el.offsetLeft,
        width: el.offsetWidth,
        height: el.offsetHeight
      };
    }
  }

  function preventDefaultException(el, exceptions) {
    for (var i in exceptions) {
      if (exceptions[i].test(el[i])) {
        return true;
      }
    }
    return false;
  }

  function tap(e, eventName) {
    var ev = document.createEvent('Event');
    ev.initEvent(eventName, true, true);
    ev.pageX = e.pageX;
    ev.pageY = e.pageY;
    e.target.dispatchEvent(ev);
  }

  function click(e) {
    var event = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'click';

    var eventSource = void 0;
    if (e.type === 'mouseup' || e.type === 'mousecancel') {
      eventSource = e;
    } else if (e.type === 'touchend' || e.type === 'touchcancel') {
      eventSource = e.changedTouches[0];
    }
    var posSrc = {};
    if (eventSource) {
      posSrc.screenX = eventSource.screenX || 0;
      posSrc.screenY = eventSource.screenY || 0;
      posSrc.clientX = eventSource.clientX || 0;
      posSrc.clientY = eventSource.clientY || 0;
    }
    var ev = void 0;
    var bubbles = true;
    var cancelable = true;
    if (typeof MouseEvent !== 'undefined') {
      try {
        ev = new MouseEvent(event, extend({
          bubbles: bubbles,
          cancelable: cancelable
        }, posSrc));
      } catch (e) {
        createEvent();
      }
    } else {
      createEvent();
    }

    function createEvent() {
      ev = document.createEvent('Event');
      ev.initEvent(event, bubbles, cancelable);
      extend(ev, posSrc);
    }

    // forwardedTouchEvent set to true in case of the conflict with fastclick
    ev.forwardedTouchEvent = true;
    ev._constructed = true;
    e.target.dispatchEvent(ev);
  }

  function dblclick(e) {
    click(e, 'dblclick');
  }

  function prepend(el, target) {
    if (target.firstChild) {
      before(el, target.firstChild);
    } else {
      target.appendChild(el);
    }
  }

  function before(el, target) {
    target.parentNode.insertBefore(el, target);
  }

  function removeChild(el, child) {
    el.removeChild(child);
  }

  var DEFAULT_OPTIONS = {
    startX: 0,
    startY: 0,
    scrollX: false,
    scrollY: true,
    freeScroll: false,
    directionLockThreshold: 5,
    eventPassthrough: '',
    click: false,
    tap: false,
    /**
     * support any side
     * bounce: {
     *   top: true,
     *   bottom: true,
     *   left: true,
     *   right: true
     * }
     */
    bounce: true,
    bounceTime: 800,
    momentum: true,
    momentumLimitTime: 300,
    momentumLimitDistance: 15,
    swipeTime: 2500,
    swipeBounceTime: 500,
    deceleration: 0.0015,
    flickLimitTime: 200,
    flickLimitDistance: 100,
    resizePolling: 60,
    probeType: 0,
    preventDefault: true,
    preventDefaultException: {
      tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT|AUDIO)$/
    },
    HWCompositing: true,
    useTransition: true,
    useTransform: true,
    bindToWrapper: false,
    disableMouse: hasTouch,
    disableTouch: !hasTouch,
    observeDOM: true,
    autoBlur: true,
    /**
     * for picker
     * wheel: {
     *   selectedIndex: 0,
     *   rotate: 25,
     *   adjustTime: 400
     *   wheelWrapperClass: 'wheel-scroll',
     *   wheelItemClass: 'wheel-item'
     * }
     */
    wheel: false,
    /**
     * for slide
     * snap: {
     *   loop: false,
     *   el: domEl,
     *   threshold: 0.1,
     *   stepX: 100,
     *   stepY: 100,
     *   speed: 400,
     *   easing: {
     *     style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
     *     fn: function (t) {
     *       return t * (2 - t)
     *     }
     *   }
     *   listenFlick: true
     * }
     */
    snap: false,
    /**
     * for scrollbar
     * scrollbar: {
     *   fade: true,
     *   interactive: false
     * }
     */
    scrollbar: false,
    /**
     * for pull down and refresh
     * pullDownRefresh: {
     *   threshold: 50,
     *   stop: 20
     * }
     */
    pullDownRefresh: false,
    /**
     * for pull up and load
     * pullUpLoad: {
     *   threshold: 50
     * }
     */
    pullUpLoad: false,
    /**
     * for mouse wheel
     * mouseWheel: {
     *   speed: 20,
     *   invert: false,
     *   easeTime: 300
     * }
     */
    mouseWheel: false,
    stopPropagation: false,
    /**
     * for zoom
     * zoom: {
     *   start: 1,
     *   min: 1,
     *   max: 4
     * }
     */
    zoom: false,
    /**
     * for infinity
     * infinity: {
     *   render(item, div) {
     *   },
     *   createTombstone() {
     *   },
     *   fetch(count) {
     *   }
     * }
     */
    infinity: false,
    /**
     * for double click
     * dblclick: {
     *   delay: 300
     * }
     */
    dblclick: false
  };

  function initMixin(BScroll) {
    BScroll.prototype._init = function (options) {
      this._handleOptions(options);

      // init private custom events
      this._events = {};

      this.x = 0;
      this.y = 0;
      this.directionX = 0;
      this.directionY = 0;

      this.setScale(1);

      this._addDOMEvents();

      this._initExtFeatures();

      this._watchTransition();

      if (this.options.observeDOM) {
        this._initDOMObserver();
      }

      if (this.options.autoBlur) {
        this._handleAutoBlur();
      }

      this.refresh();

      if (!this.options.snap) {
        this.scrollTo(this.options.startX, this.options.startY);
      }

      this.enable();
    };

    BScroll.prototype.setScale = function (scale) {
      this.lastScale = isUndef(this.scale) ? scale : this.scale;
      this.scale = scale;
    };

    BScroll.prototype._handleOptions = function (options) {
      this.options = extend({}, DEFAULT_OPTIONS, options);

      this.translateZ = this.options.HWCompositing && hasPerspective ? ' translateZ(0)' : '';

      this.options.useTransition = this.options.useTransition && hasTransition;
      this.options.useTransform = this.options.useTransform && hasTransform;

      this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;

      // If you want eventPassthrough I have to lock one of the axes
      this.options.scrollX = this.options.eventPassthrough === 'horizontal' ? false : this.options.scrollX;
      this.options.scrollY = this.options.eventPassthrough === 'vertical' ? false : this.options.scrollY;

      // With eventPassthrough we also need lockDirection mechanism
      this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
      this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;

      if (this.options.tap === true) {
        this.options.tap = 'tap';
      }
    };

    BScroll.prototype._addDOMEvents = function () {
      var eventOperation = addEvent;
      this._handleDOMEvents(eventOperation);
    };

    BScroll.prototype._removeDOMEvents = function () {
      var eventOperation = removeEvent;
      this._handleDOMEvents(eventOperation);
    };

    BScroll.prototype._handleDOMEvents = function (eventOperation) {
      var target = this.options.bindToWrapper ? this.wrapper : window;
      eventOperation(window, 'orientationchange', this);
      eventOperation(window, 'resize', this);

      if (this.options.click) {
        eventOperation(this.wrapper, 'click', this, true);
      }

      if (!this.options.disableMouse) {
        eventOperation(this.wrapper, 'mousedown', this);
        eventOperation(target, 'mousemove', this);
        eventOperation(target, 'mousecancel', this);
        eventOperation(target, 'mouseup', this);
      }

      if (hasTouch && !this.options.disableTouch) {
        eventOperation(this.wrapper, 'touchstart', this);
        eventOperation(target, 'touchmove', this);
        eventOperation(target, 'touchcancel', this);
        eventOperation(target, 'touchend', this);
      }

      eventOperation(this.scroller, style.transitionEnd, this);
    };

    BScroll.prototype._initExtFeatures = function () {
      if (this.options.snap) {
        this._initSnap();
      }
      if (this.options.scrollbar) {
        this._initScrollbar();
      }
      if (this.options.pullUpLoad) {
        this._initPullUp();
      }
      if (this.options.pullDownRefresh) {
        this._initPullDown();
      }
      if (this.options.wheel) {
        this._initWheel();
      }
      if (this.options.mouseWheel) {
        this._initMouseWheel();
      }
      if (this.options.zoom) {
        this._initZoom();
      }
      if (this.options.infinity) {
        this._initInfinite();
      }
    };

    BScroll.prototype._watchTransition = function () {
      if (typeof Object.defineProperty !== 'function') {
        return;
      }
      var me = this;
      var isInTransition = false;
      var key = this.options.useTransition ? 'isInTransition' : 'isAnimating';
      Object.defineProperty(this, key, {
        get: function get() {
          return isInTransition;
        },
        set: function set(newVal) {
          isInTransition = newVal;
          // fix issue #359
          var el = me.scroller.children.length ? me.scroller.children : [me.scroller];
          var pointerEvents = isInTransition && !me.pulling ? 'none' : 'auto';
          for (var i = 0; i < el.length; i++) {
            el[i].style.pointerEvents = pointerEvents;
          }
        }
      });
    };

    BScroll.prototype._handleAutoBlur = function () {
      this.on('scrollStart', function () {
        var activeElement = document.activeElement;
        if (activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA')) {
          activeElement.blur();
        }
      });
    };

    BScroll.prototype._initDOMObserver = function () {
      var _this = this;

      if (typeof MutationObserver !== 'undefined') {
        var timer = void 0;
        var observer = new MutationObserver(function (mutations) {
          // don't do any refresh during the transition, or outside of the boundaries
          if (_this._shouldNotRefresh()) {
            return;
          }
          var immediateRefresh = false;
          var deferredRefresh = false;
          for (var i = 0; i < mutations.length; i++) {
            var mutation = mutations[i];
            if (mutation.type !== 'attributes') {
              immediateRefresh = true;
              break;
            } else {
              if (mutation.target !== _this.scroller) {
                deferredRefresh = true;
                break;
              }
            }
          }
          if (immediateRefresh) {
            _this.refresh();
          } else if (deferredRefresh) {
            // attributes changes too often
            clearTimeout(timer);
            timer = setTimeout(function () {
              if (!_this._shouldNotRefresh()) {
                _this.refresh();
              }
            }, 60);
          }
        });
        var config = {
          attributes: true,
          childList: true,
          subtree: true
        };
        observer.observe(this.scroller, config);

        this.on('destroy', function () {
          observer.disconnect();
        });
      } else {
        this._checkDOMUpdate();
      }
    };

    BScroll.prototype._shouldNotRefresh = function () {
      var outsideBoundaries = this.x > this.minScrollX || this.x < this.maxScrollX || this.y > this.minScrollY || this.y < this.maxScrollY;

      return this.isInTransition || this.stopFromTransition || outsideBoundaries;
    };

    BScroll.prototype._checkDOMUpdate = function () {
      var scrollerRect = getRect(this.scroller);
      var oldWidth = scrollerRect.width;
      var oldHeight = scrollerRect.height;

      function check() {
        if (this.destroyed) {
          return;
        }
        scrollerRect = getRect(this.scroller);
        var newWidth = scrollerRect.width;
        var newHeight = scrollerRect.height;

        if (oldWidth !== newWidth || oldHeight !== newHeight) {
          this.refresh();
        }
        oldWidth = newWidth;
        oldHeight = newHeight;

        next.call(this);
      }

      function next() {
        var _this2 = this;

        setTimeout(function () {
          check.call(_this2);
        }, 1000);
      }

      next.call(this);
    };

    BScroll.prototype.handleEvent = function (e) {
      switch (e.type) {
        case 'touchstart':
        case 'mousedown':
          this._start(e);
          if (this.options.zoom && e.touches && e.touches.length > 1) {
            this._zoomStart(e);
          }
          break;
        case 'touchmove':
        case 'mousemove':
          if (this.options.zoom && e.touches && e.touches.length > 1) {
            this._zoom(e);
          } else {
            this._move(e);
          }
          break;
        case 'touchend':
        case 'mouseup':
        case 'touchcancel':
        case 'mousecancel':
          if (this.scaled) {
            this._zoomEnd(e);
          } else {
            this._end(e);
          }
          break;
        case 'orientationchange':
        case 'resize':
          this._resize();
          break;
        case 'transitionend':
        case 'webkitTransitionEnd':
        case 'oTransitionEnd':
        case 'MSTransitionEnd':
          this._transitionEnd(e);
          break;
        case 'click':
          if (this.enabled && !e._constructed) {
            if (!preventDefaultException(e.target, this.options.preventDefaultException)) {
              e.preventDefault();
              e.stopPropagation();
            }
          }
          break;
        case 'wheel':
        case 'DOMMouseScroll':
        case 'mousewheel':
          this._onMouseWheel(e);
          break;
      }
    };

    BScroll.prototype.refresh = function () {
      var isWrapperStatic = window.getComputedStyle(this.wrapper, null).position === 'static';
      var wrapperRect = getRect(this.wrapper);
      this.wrapperWidth = wrapperRect.width;
      this.wrapperHeight = wrapperRect.height;

      var scrollerRect = getRect(this.scroller);
      this.scrollerWidth = Math.round(scrollerRect.width * this.scale);
      this.scrollerHeight = Math.round(scrollerRect.height * this.scale);

      this.relativeX = scrollerRect.left;
      this.relativeY = scrollerRect.top;

      if (isWrapperStatic) {
        this.relativeX -= wrapperRect.left;
        this.relativeY -= wrapperRect.top;
      }

      this.minScrollX = 0;
      this.minScrollY = 0;

      var wheel = this.options.wheel;
      if (wheel) {
        this.items = this.scroller.children;
        // check whether there are all disable items or not when refresh
        this._checkWheelAllDisabled();
        this.options.itemHeight = this.itemHeight = this.items.length ? this.scrollerHeight / this.items.length : 0;
        if (this.selectedIndex === undefined) {
          this.selectedIndex = wheel.selectedIndex || 0;
        }
        this.options.startY = -this.selectedIndex * this.itemHeight;

        this.maxScrollX = 0;
        this.maxScrollY = -this.itemHeight * (this.items.length - 1);
      } else {
        this.maxScrollX = this.wrapperWidth - this.scrollerWidth;
        if (!this.options.infinity) {
          this.maxScrollY = this.wrapperHeight - this.scrollerHeight;
        }
        if (this.maxScrollX < 0) {
          this.maxScrollX -= this.relativeX;
          this.minScrollX = -this.relativeX;
        } else if (this.scale > 1) {
          this.maxScrollX = this.maxScrollX / 2 - this.relativeX;
          this.minScrollX = this.maxScrollX;
        }
        if (this.maxScrollY < 0) {
          this.maxScrollY -= this.relativeY;
          this.minScrollY = -this.relativeY;
        } else if (this.scale > 1) {
          this.maxScrollY = this.maxScrollY / 2 - this.relativeY;
          this.minScrollY = this.maxScrollY;
        }
      }

      this.hasHorizontalScroll = this.options.scrollX && this.maxScrollX < this.minScrollX;
      this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < this.minScrollY;

      if (!this.hasHorizontalScroll) {
        this.maxScrollX = this.minScrollX;
        this.scrollerWidth = this.wrapperWidth;
      }

      if (!this.hasVerticalScroll) {
        this.maxScrollY = this.minScrollY;
        this.scrollerHeight = this.wrapperHeight;
      }

      this.endTime = 0;
      this.directionX = 0;
      this.directionY = 0;
      this.wrapperOffset = offset(this.wrapper);

      this.trigger('refresh');

      !this.scaled && this.resetPosition();
    };

    BScroll.prototype.enable = function () {
      this.enabled = true;
    };

    BScroll.prototype.disable = function () {
      this.enabled = false;
    };
  }

  var ease = {
    // easeOutQuint
    swipe: {
      style: 'cubic-bezier(0.23, 1, 0.32, 1)',
      fn: function fn(t) {
        return 1 + --t * t * t * t * t;
      }
    },
    // easeOutQuard
    swipeBounce: {
      style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
      fn: function fn(t) {
        return t * (2 - t);
      }
    },
    // easeOutQuart
    bounce: {
      style: 'cubic-bezier(0.165, 0.84, 0.44, 1)',
      fn: function fn(t) {
        return 1 - --t * t * t * t;
      }
    }
  };

  function momentum(current, start, time, lowerMargin, upperMargin, wrapperSize, options, scroll) {
    var distance = current - start;
    var speed = Math.abs(distance) / time;

    var deceleration = options.deceleration,
      itemHeight = options.itemHeight,
      swipeBounceTime = options.swipeBounceTime,
      wheel = options.wheel,
      swipeTime = options.swipeTime;

    var duration = swipeTime;
    var rate = wheel ? 4 : 15;

    var destination = current + speed / deceleration * (distance < 0 ? -1 : 1);

    if (wheel && itemHeight) {
      destination = scroll._findNearestValidWheel(destination).y;
    }

    if (destination < lowerMargin) {
      destination = wrapperSize ? Math.max(lowerMargin - wrapperSize / 4, lowerMargin - wrapperSize / rate * speed) : lowerMargin;
      duration = swipeBounceTime;
    } else if (destination > upperMargin) {
      destination = wrapperSize ? Math.min(upperMargin + wrapperSize / 4, upperMargin + wrapperSize / rate * speed) : upperMargin;
      duration = swipeBounceTime;
    }

    return {
      destination: Math.round(destination),
      duration: duration
    };
  }

  var DEFAULT_INTERVAL = 100 / 60;

  function noop() { }

  var requestAnimationFrame = function () {
    if (!inBrowser) {
      /* istanbul ignore if */
      return noop;
    }
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame ||
      // if all else fails, use setTimeout
      function (callback) {
        return window.setTimeout(callback, (callback.interval || DEFAULT_INTERVAL) / 2); // make interval as precise as possible.
      };
  }();

  var cancelAnimationFrame = function () {
    if (!inBrowser) {
      /* istanbul ignore if */
      return noop;
    }
    return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || function (id) {
      window.clearTimeout(id);
    };
  }();

  var DIRECTION_UP = 1;
  var DIRECTION_DOWN = -1;
  var DIRECTION_LEFT = 1;
  var DIRECTION_RIGHT = -1;

  var PROBE_DEBOUNCE = 1;

  var PROBE_REALTIME = 3;

  function warn(msg) {
    console.error('[BScroll warn]: ' + msg);
  }

  function assert(condition, msg) {
    if (!condition) {
      throw new Error('[BScroll] ' + msg);
    }
  }

  function coreMixin(BScroll) {
    BScroll.prototype._start = function (e) {
      var _eventType = eventType[e.type];
      if (_eventType !== TOUCH_EVENT) {
        if (e.button !== 0) {
          return;
        }
      }
      if (!this.enabled || this.destroyed || this.initiated && this.initiated !== _eventType) {
        return;
      }
      this.initiated = _eventType;

      if (this.options.preventDefault && !preventDefaultException(e.target, this.options.preventDefaultException)) {
        e.preventDefault();
      }
      if (this.options.stopPropagation) {
        e.stopPropagation();
      }

      this.moved = false;
      this.distX = 0;
      this.distY = 0;
      this.directionX = 0;
      this.directionY = 0;
      this.movingDirectionX = 0;
      this.movingDirectionY = 0;
      this.directionLocked = 0;

      this._transitionTime();
      this.startTime = getNow();

      if (this.options.wheel) {
        this.target = e.target;
      }

      this.stop();

      var point = e.touches ? e.touches[0] : e;

      this.startX = this.x;
      this.startY = this.y;
      this.absStartX = this.x;
      this.absStartY = this.y;
      this.pointX = point.pageX;
      this.pointY = point.pageY;

      this.trigger('beforeScrollStart');
    };

    BScroll.prototype._move = function (e) {
      if (!this.enabled || this.destroyed || eventType[e.type] !== this.initiated) {
        return;
      }

      if (this.options.preventDefault) {
        e.preventDefault();
      }
      if (this.options.stopPropagation) {
        e.stopPropagation();
      }

      var point = e.touches ? e.touches[0] : e;
      var deltaX = point.pageX - this.pointX;
      var deltaY = point.pageY - this.pointY;

      this.pointX = point.pageX;
      this.pointY = point.pageY;

      this.distX += deltaX;
      this.distY += deltaY;

      var absDistX = Math.abs(this.distX);
      var absDistY = Math.abs(this.distY);

      var timestamp = getNow();

      // We need to move at least momentumLimitDistance pixels for the scrolling to initiate
      if (timestamp - this.endTime > this.options.momentumLimitTime && !this.moved && absDistY < this.options.momentumLimitDistance && absDistX < this.options.momentumLimitDistance) {
        return;
      }

      // If you are scrolling in one direction lock the other
      if (!this.directionLocked && !this.options.freeScroll) {
        if (absDistX > absDistY + this.options.directionLockThreshold) {
          this.directionLocked = 'h'; // lock horizontally
        } else if (absDistY >= absDistX + this.options.directionLockThreshold) {
          this.directionLocked = 'v'; // lock vertically
        } else {
          this.directionLocked = 'n'; // no lock
        }
      }

      if (this.directionLocked === 'h') {
        if (this.options.eventPassthrough === 'vertical') {
          e.preventDefault();
        } else if (this.options.eventPassthrough === 'horizontal') {
          this.initiated = false;
          return;
        }
        deltaY = 0;
      } else if (this.directionLocked === 'v') {
        if (this.options.eventPassthrough === 'horizontal') {
          e.preventDefault();
        } else if (this.options.eventPassthrough === 'vertical') {
          this.initiated = false;
          return;
        }
        deltaX = 0;
      }

      deltaX = this.hasHorizontalScroll ? deltaX : 0;
      deltaY = this.hasVerticalScroll ? deltaY : 0;
      this.movingDirectionX = deltaX > 0 ? DIRECTION_RIGHT : deltaX < 0 ? DIRECTION_LEFT : 0;
      this.movingDirectionY = deltaY > 0 ? DIRECTION_DOWN : deltaY < 0 ? DIRECTION_UP : 0;

      var newX = this.x + deltaX;
      var newY = this.y + deltaY;

      var top = false;
      var bottom = false;
      var left = false;
      var right = false;
      // Slow down or stop if outside of the boundaries
      var bounce = this.options.bounce;
      if (bounce !== false) {
        top = bounce.top === undefined ? true : bounce.top;
        bottom = bounce.bottom === undefined ? true : bounce.bottom;
        left = bounce.left === undefined ? true : bounce.left;
        right = bounce.right === undefined ? true : bounce.right;
      }
      if (newX > this.minScrollX || newX < this.maxScrollX) {
        if (newX > this.minScrollX && left || newX < this.maxScrollX && right) {
          newX = this.x + deltaX / 3;
        } else {
          newX = newX > this.minScrollX ? this.minScrollX : this.maxScrollX;
        }
      }
      if (newY > this.minScrollY || newY < this.maxScrollY) {
        if (newY > this.minScrollY && top || newY < this.maxScrollY && bottom) {
          newY = this.y + deltaY / 3;
        } else {
          newY = newY > this.minScrollY ? this.minScrollY : this.maxScrollY;
        }
      }

      if (!this.moved) {
        this.moved = true;
        this.trigger('scrollStart');
      }

      this._translate(newX, newY);

      if (timestamp - this.startTime > this.options.momentumLimitTime) {
        this.startTime = timestamp;
        this.startX = this.x;
        this.startY = this.y;

        if (this.options.probeType === PROBE_DEBOUNCE) {
          this.trigger('scroll', {
            x: this.x,
            y: this.y
          });
        }
      }

      if (this.options.probeType > PROBE_DEBOUNCE) {
        this.trigger('scroll', {
          x: this.x,
          y: this.y
        });
      }

      var scrollLeft = document.documentElement.scrollLeft || window.pageXOffset || document.body.scrollLeft;
      var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;

      var pX = this.pointX - scrollLeft;
      var pY = this.pointY - scrollTop;

      if (pX > document.documentElement.clientWidth - this.options.momentumLimitDistance || pX < this.options.momentumLimitDistance || pY < this.options.momentumLimitDistance || pY > document.documentElement.clientHeight - this.options.momentumLimitDistance) {
        this._end(e);
      }
    };

    BScroll.prototype._end = function (e) {
      if (!this.enabled || this.destroyed || eventType[e.type] !== this.initiated) {
        return;
      }
      this.initiated = false;

      if (this.options.preventDefault && !preventDefaultException(e.target, this.options.preventDefaultException)) {
        e.preventDefault();
      }
      if (this.options.stopPropagation) {
        e.stopPropagation();
      }

      this.trigger('touchEnd', {
        x: this.x,
        y: this.y
      });

      this.isInTransition = false;

      // ensures that the last position is rounded
      var newX = Math.round(this.x);
      var newY = Math.round(this.y);

      var deltaX = newX - this.absStartX;
      var deltaY = newY - this.absStartY;
      this.directionX = deltaX > 0 ? DIRECTION_RIGHT : deltaX < 0 ? DIRECTION_LEFT : 0;
      this.directionY = deltaY > 0 ? DIRECTION_DOWN : deltaY < 0 ? DIRECTION_UP : 0;

      // if configure pull down refresh, check it first
      if (this.options.pullDownRefresh && this._checkPullDown()) {
        return;
      }

      // check if it is a click operation
      if (this._checkClick(e)) {
        this.trigger('scrollCancel');
        return;
      }

      // reset if we are outside of the boundaries
      if (this.resetPosition(this.options.bounceTime, ease.bounce)) {
        return;
      }

      this._translate(newX, newY);

      this.endTime = getNow();
      var duration = this.endTime - this.startTime;
      var absDistX = Math.abs(newX - this.startX);
      var absDistY = Math.abs(newY - this.startY);

      // flick
      if (this._events.flick && duration < this.options.flickLimitTime && absDistX < this.options.flickLimitDistance && absDistY < this.options.flickLimitDistance) {
        this.trigger('flick');
        return;
      }

      var time = 0;
      // start momentum animation if needed
      if (this.options.momentum && duration < this.options.momentumLimitTime && (absDistY > this.options.momentumLimitDistance || absDistX > this.options.momentumLimitDistance)) {
        var top = false;
        var bottom = false;
        var left = false;
        var right = false;
        var bounce = this.options.bounce;
        if (bounce !== false) {
          top = bounce.top === undefined ? true : bounce.top;
          bottom = bounce.bottom === undefined ? true : bounce.bottom;
          left = bounce.left === undefined ? true : bounce.left;
          right = bounce.right === undefined ? true : bounce.right;
        }
        var wrapperWidth = this.directionX === DIRECTION_RIGHT && left || this.directionX === DIRECTION_LEFT && right ? this.wrapperWidth : 0;
        var wrapperHeight = this.directionY === DIRECTION_DOWN && top || this.directionY === DIRECTION_UP && bottom ? this.wrapperHeight : 0;
        var momentumX = this.hasHorizontalScroll ? momentum(this.x, this.startX, duration, this.maxScrollX, this.minScrollX, wrapperWidth, this.options, this) : { destination: newX, duration: 0 };
        var momentumY = this.hasVerticalScroll ? momentum(this.y, this.startY, duration, this.maxScrollY, this.minScrollY, wrapperHeight, this.options, this) : { destination: newY, duration: 0 };
        newX = momentumX.destination;
        newY = momentumY.destination;
        time = Math.max(momentumX.duration, momentumY.duration);
        this.isInTransition = true;
      } else {
        if (this.options.wheel) {
          newY = this._findNearestValidWheel(newY).y;
          time = this.options.wheel.adjustTime || 400;
        }
      }

      var easing = ease.swipe;
      if (this.options.snap) {
        var snap = this._nearestSnap(newX, newY);
        this.currentPage = snap;
        time = this.options.snapSpeed || Math.max(Math.max(Math.min(Math.abs(newX - snap.x), 1000), Math.min(Math.abs(newY - snap.y), 1000)), 300);
        newX = snap.x;
        newY = snap.y;

        this.directionX = 0;
        this.directionY = 0;
        easing = this.options.snap.easing || ease.bounce;
      }

      if (newX !== this.x || newY !== this.y) {
        // change easing function when scroller goes out of the boundaries
        if (newX > this.minScrollX || newX < this.maxScrollX || newY > this.minScrollY || newY < this.maxScrollY) {
          easing = ease.swipeBounce;
        }
        this.scrollTo(newX, newY, time, easing);
        return;
      }

      if (this.options.wheel) {
        this.selectedIndex = this._findNearestValidWheel(this.y).index;
      }

      this.trigger('scrollEnd', {
        x: this.x,
        y: this.y
      });
    };

    BScroll.prototype._checkClick = function (e) {
      // when in the process of pulling down, it should not prevent click
      var preventClick = this.stopFromTransition && !this.pulling;
      this.stopFromTransition = false;

      // we scrolled less than 15 pixels
      if (!this.moved) {
        if (this.options.wheel) {
          if (this.target && this.target.className === this.options.wheel.wheelWrapperClass) {
            var index = this._findNearestValidWheel(this.y).index;
            var _offset = Math.round((this.pointY + offsetToBody(this.wrapper).top - this.wrapperHeight / 2) / this.itemHeight);
            this.target = this.items[index + _offset];
          }
          var top = offset(this.target).top;
          var left = offset(this.target).left;
          top -= this.wrapperOffset.top;
          top -= Math.round(this.target.offsetHeight / 2 - this.wrapper.offsetHeight / 2) || 0;
          left -= this.wrapperOffset.left;
          left -= Math.round(this.target.offsetWidth / 2 - this.wrapper.offsetWidth / 2) || 0;

          top = this._findNearestValidWheel(top).y;
          this.scrollTo(left, top, this.options.wheel.adjustTime || 400, ease.swipe);
          return true;
        } else {
          if (!preventClick) {
            var _dblclick = this.options.dblclick;
            var dblclickTrigged = false;
            if (_dblclick && this.lastClickTime) {
              var _dblclick$delay = _dblclick.delay,
                delay = _dblclick$delay === undefined ? 300 : _dblclick$delay;

              if (getNow() - this.lastClickTime < delay) {
                dblclickTrigged = true;
                dblclick(e);
              }
            }
            if (this.options.tap) {
              tap(e, this.options.tap);
            }

            if (this.options.click && !preventDefaultException(e.target, this.options.preventDefaultException)) {
              click(e);
            }
            this.lastClickTime = dblclickTrigged ? null : getNow();
            return true;
          }
          return false;
        }
      }
      return false;
    };

    BScroll.prototype._resize = function () {
      var _this = this;

      if (!this.enabled) {
        return;
      }
      // fix a scroll problem under Android condition
      if (isAndroid) {
        this.wrapper.scrollTop = 0;
      }
      clearTimeout(this.resizeTimeout);
      this.resizeTimeout = setTimeout(function () {
        _this.refresh();
      }, this.options.resizePolling);
    };

    BScroll.prototype._startProbe = function () {
      cancelAnimationFrame(this.probeTimer);
      this.probeTimer = requestAnimationFrame(probe);

      var me = this;

      function probe() {
        var pos = me.getComputedPosition();
        me.trigger('scroll', pos);
        if (!me.isInTransition) {
          me.trigger('scrollEnd', pos);
          return;
        }
        me.probeTimer = requestAnimationFrame(probe);
      }
    };

    BScroll.prototype._transitionTime = function () {
      var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;

      this.scrollerStyle[style.transitionDuration] = time + 'ms';

      if (this.options.wheel) {
        for (var i = 0; i < this.items.length; i++) {
          this.items[i].style[style.transitionDuration] = time + 'ms';
        }
      }

      if (this.indicators) {
        for (var _i = 0; _i < this.indicators.length; _i++) {
          this.indicators[_i].transitionTime(time);
        }
      }
    };

    BScroll.prototype._transitionTimingFunction = function (easing) {
      this.scrollerStyle[style.transitionTimingFunction] = easing;

      if (this.options.wheel) {
        for (var i = 0; i < this.items.length; i++) {
          this.items[i].style[style.transitionTimingFunction] = easing;
        }
      }

      if (this.indicators) {
        for (var _i2 = 0; _i2 < this.indicators.length; _i2++) {
          this.indicators[_i2].transitionTimingFunction(easing);
        }
      }
    };

    BScroll.prototype._transitionEnd = function (e) {
      if (e.target !== this.scroller || !this.isInTransition) {
        return;
      }

      this._transitionTime();
      var needReset = !this.pulling || this.movingDirectionY === DIRECTION_UP;
      if (needReset && !this.resetPosition(this.options.bounceTime, ease.bounce)) {
        this.isInTransition = false;
        if (this.options.probeType !== PROBE_REALTIME) {
          this.trigger('scrollEnd', {
            x: this.x,
            y: this.y
          });
        }
      }
    };

    BScroll.prototype._translate = function (x, y, scale) {
      assert(!isUndef(x) && !isUndef(y), 'Translate x or y is null or undefined.');
      if (isUndef(scale)) {
        scale = this.scale;
      }
      if (this.options.useTransform) {
        this.scrollerStyle[style.transform] = 'translate(' + x + 'px,' + y + 'px) scale(' + scale + ')' + this.translateZ;
      } else {
        x = Math.round(x);
        y = Math.round(y);
        this.scrollerStyle.left = x + 'px';
        this.scrollerStyle.top = y + 'px';
      }

      if (this.options.wheel) {
        var _options$wheel$rotate = this.options.wheel.rotate,
          rotate = _options$wheel$rotate === undefined ? 25 : _options$wheel$rotate;

        for (var i = 0; i < this.items.length; i++) {
          var deg = rotate * (y / this.itemHeight + i);
          this.items[i].style[style.transform] = 'rotateX(' + deg + 'deg)';
        }
      }

      this.x = x;
      this.y = y;
      this.setScale(scale);

      if (this.indicators) {
        for (var _i3 = 0; _i3 < this.indicators.length; _i3++) {
          this.indicators[_i3].updatePosition();
        }
      }
    };

    BScroll.prototype._animate = function (destX, destY, duration, easingFn) {
      var me = this;
      var startX = this.x;
      var startY = this.y;
      var startScale = this.lastScale;
      var destScale = this.scale;
      var startTime = getNow();
      var destTime = startTime + duration;

      function step() {
        var now = getNow();

        if (now >= destTime) {
          me.isAnimating = false;
          me._translate(destX, destY, destScale);

          me.trigger('scroll', {
            x: me.x,
            y: me.y
          });

          if (!me.pulling && !me.resetPosition(me.options.bounceTime)) {
            me.trigger('scrollEnd', {
              x: me.x,
              y: me.y
            });
          }
          return;
        }
        now = (now - startTime) / duration;
        var easing = easingFn(now);
        var newX = (destX - startX) * easing + startX;
        var newY = (destY - startY) * easing + startY;
        var newScale = (destScale - startScale) * easing + startScale;

        me._translate(newX, newY, newScale);

        if (me.isAnimating) {
          me.animateTimer = requestAnimationFrame(step);
        }

        if (me.options.probeType === PROBE_REALTIME) {
          me.trigger('scroll', {
            x: me.x,
            y: me.y
          });
        }
      }

      this.isAnimating = true;
      cancelAnimationFrame(this.animateTimer);
      step();
    };

    BScroll.prototype.scrollBy = function (x, y) {
      var time = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
      var easing = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ease.bounce;

      x = this.x + x;
      y = this.y + y;

      this.scrollTo(x, y, time, easing);
    };

    BScroll.prototype.scrollTo = function (x, y) {
      var time = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
      var easing = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ease.bounce;

      if (this.options.wheel) {
        y = this._findNearestValidWheel(y).y;
      }
      this.isInTransition = this.options.useTransition && time > 0 && (this.x !== x || this.y !== y);

      if (!time || this.options.useTransition) {
        this._transitionTimingFunction(easing.style);
        this._transitionTime(time);
        this._translate(x, y);

        if (time && this.options.probeType === PROBE_REALTIME) {
          this._startProbe();
        }
        if (!time) {
          // don't trigger resetPosition when zoom feature is open, fix #748
          if (this.options.zoom) return;
          this.trigger('scroll', {
            x: x,
            y: y
          });
          // force reflow to put everything in position
          this._reflow = document.body.offsetHeight;
          if (!this.resetPosition(this.options.bounceTime, ease.bounce)) {
            this.trigger('scrollEnd', {
              x: x,
              y: y
            });
          }
        }

        if (this.options.wheel) {
          this.selectedIndex = this._findNearestValidWheel(y).index;
        }
      } else {
        this._animate(x, y, time, easing.fn);
      }
    };

    BScroll.prototype.scrollToElement = function (el, time, offsetX, offsetY, easing) {
      if (!el) {
        return;
      }
      el = el.nodeType ? el : this.scroller.querySelector(el);

      if (this.options.wheel && !el.classList.contains(this.options.wheel.wheelItemClass)) {
        return;
      }

      var pos = offset(el);
      pos.left -= this.wrapperOffset.left;
      pos.top -= this.wrapperOffset.top;

      // if offsetX/Y are true we center the element to the screen
      if (offsetX === true) {
        offsetX = Math.round(el.offsetWidth / 2 - this.wrapper.offsetWidth / 2);
      }
      if (offsetY === true) {
        offsetY = Math.round(el.offsetHeight / 2 - this.wrapper.offsetHeight / 2);
      }

      pos.left -= offsetX || 0;
      pos.top -= offsetY || 0;
      pos.left = pos.left > this.minScrollX ? this.minScrollX : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
      pos.top = pos.top > this.minScrollY ? this.minScrollY : pos.top < this.maxScrollY ? this.maxScrollY : pos.top;

      if (this.options.wheel) {
        pos.top = this._findNearestValidWheel(pos.top).y;
      }

      this.scrollTo(pos.left, pos.top, time, easing);
    };

    BScroll.prototype.resetPosition = function () {
      var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
      var easeing = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ease.bounce;

      var x = this.x;
      var roundX = Math.round(x);
      if (!this.hasHorizontalScroll || roundX > this.minScrollX) {
        x = this.minScrollX;
      } else if (roundX < this.maxScrollX) {
        x = this.maxScrollX;
      }

      var y = this.y;
      var roundY = Math.round(y);
      if (!this.hasVerticalScroll || roundY > this.minScrollY) {
        y = this.minScrollY;
      } else if (roundY < this.maxScrollY) {
        y = this.maxScrollY;
      }

      if (x === this.x && y === this.y) {
        return false;
      }

      this.scrollTo(x, y, time, easeing);

      return true;
    };

    BScroll.prototype.getComputedPosition = function () {
      var matrix = window.getComputedStyle(this.scroller, null);
      var x = void 0;
      var y = void 0;

      if (this.options.useTransform) {
        matrix = matrix[style.transform].split(')')[0].split(', ');
        x = +(matrix[12] || matrix[4]);
        y = +(matrix[13] || matrix[5]);
      } else {
        x = +matrix.left.replace(/[^-\d.]/g, '');
        y = +matrix.top.replace(/[^-\d.]/g, '');
      }

      return {
        x: x,
        y: y
      };
    };

    BScroll.prototype.stop = function () {
      if (this.options.useTransition && this.isInTransition) {
        this.isInTransition = false;
        cancelAnimationFrame(this.probeTimer);
        var pos = this.getComputedPosition();
        this._translate(pos.x, pos.y);
        if (this.options.wheel) {
          this.target = this.items[this._findNearestValidWheel(pos.y).index];
        } else {
          this.trigger('scrollEnd', {
            x: this.x,
            y: this.y
          });
        }
        this.stopFromTransition = true;
      } else if (!this.options.useTransition && this.isAnimating) {
        this.isAnimating = false;
        cancelAnimationFrame(this.animateTimer);
        this.trigger('scrollEnd', {
          x: this.x,
          y: this.y
        });
        this.stopFromTransition = true;
      }
    };

    BScroll.prototype.destroy = function () {
      this.destroyed = true;
      this.trigger('destroy');
      if (this.options.useTransition) {
        cancelAnimationFrame(this.probeTimer);
      } else {
        cancelAnimationFrame(this.animateTimer);
      }
      this._removeDOMEvents();
      // remove custom events
      this._events = {};
    };
  }

  function snapMixin(BScroll) {
    BScroll.prototype._initSnap = function () {
      var _this = this;

      this.currentPage = {};
      var snap = this.options.snap;

      if (snap.loop) {
        var children = this.scroller.children;
        if (children.length > 1) {
          prepend(children[children.length - 1].cloneNode(true), this.scroller);
          this.scroller.appendChild(children[1].cloneNode(true));
        } else {
          // Loop does not make any sense if there is only one child.
          snap.loop = false;
        }
      }

      var el = snap.el;
      if (typeof el === 'string') {
        el = this.scroller.querySelectorAll(el);
      }

      this.on('refresh', function () {
        _this.pages = [];

        if (!_this.wrapperWidth || !_this.wrapperHeight || !_this.scrollerWidth || !_this.scrollerHeight) {
          return;
        }

        var stepX = snap.stepX || _this.wrapperWidth;
        var stepY = snap.stepY || _this.wrapperHeight;

        var x = 0;
        var y = void 0;
        var cx = void 0;
        var cy = void 0;
        var i = 0;
        var l = void 0;
        var m = 0;
        var n = void 0;
        var rect = void 0;
        if (!el) {
          cx = Math.round(stepX / 2);
          cy = Math.round(stepY / 2);

          while (x > -_this.scrollerWidth) {
            _this.pages[i] = [];
            l = 0;
            y = 0;

            while (y > -_this.scrollerHeight) {
              _this.pages[i][l] = {
                x: Math.max(x, _this.maxScrollX),
                y: Math.max(y, _this.maxScrollY),
                width: stepX,
                height: stepY,
                cx: x - cx,
                cy: y - cy
              };

              y -= stepY;
              l++;
            }

            x -= stepX;
            i++;
          }
        } else {
          l = el.length;
          n = -1;

          for (; i < l; i++) {
            rect = getRect(el[i]);
            if (i === 0 || rect.left <= getRect(el[i - 1]).left) {
              m = 0;
              n++;
            }

            if (!_this.pages[m]) {
              _this.pages[m] = [];
            }

            x = Math.max(-rect.left, _this.maxScrollX);
            y = Math.max(-rect.top, _this.maxScrollY);
            cx = x - Math.round(rect.width / 2);
            cy = y - Math.round(rect.height / 2);

            _this.pages[m][n] = {
              x: x,
              y: y,
              width: rect.width,
              height: rect.height,
              cx: cx,
              cy: cy
            };

            if (x > _this.maxScrollX) {
              m++;
            }
          }
        }

        _this._checkSnapLoop();

        var initPageX = snap._loopX ? 1 : 0;
        var initPageY = snap._loopY ? 1 : 0;
        _this._goToPage(_this.currentPage.pageX || initPageX, _this.currentPage.pageY || initPageY, 0);

        // Update snap threshold if needed.
        var snapThreshold = snap.threshold;
        if (snapThreshold % 1 === 0) {
          _this.snapThresholdX = snapThreshold;
          _this.snapThresholdY = snapThreshold;
        } else {
          _this.snapThresholdX = Math.round(_this.pages[_this.currentPage.pageX][_this.currentPage.pageY].width * snapThreshold);
          _this.snapThresholdY = Math.round(_this.pages[_this.currentPage.pageX][_this.currentPage.pageY].height * snapThreshold);
        }
      });

      this.on('scrollEnd', function () {
        if (snap.loop) {
          if (snap._loopX) {
            if (_this.currentPage.pageX === 0) {
              _this._goToPage(_this.pages.length - 2, _this.currentPage.pageY, 0);
            }
            if (_this.currentPage.pageX === _this.pages.length - 1) {
              _this._goToPage(1, _this.currentPage.pageY, 0);
            }
          } else {
            if (_this.currentPage.pageY === 0) {
              _this._goToPage(_this.currentPage.pageX, _this.pages[0].length - 2, 0);
            }
            if (_this.currentPage.pageY === _this.pages[0].length - 1) {
              _this._goToPage(_this.currentPage.pageX, 1, 0);
            }
          }
        }
      });

      if (snap.listenFlick !== false) {
        this.on('flick', function () {
          var time = snap.speed || Math.max(Math.max(Math.min(Math.abs(_this.x - _this.startX), 1000), Math.min(Math.abs(_this.y - _this.startY), 1000)), 300);

          _this._goToPage(_this.currentPage.pageX + _this.directionX, _this.currentPage.pageY + _this.directionY, time);
        });
      }

      this.on('destroy', function () {
        if (snap.loop) {
          var _children = _this.scroller.children;
          if (_children.length > 2) {
            removeChild(_this.scroller, _children[_children.length - 1]);
            removeChild(_this.scroller, _children[0]);
          }
        }
      });
    };

    BScroll.prototype._checkSnapLoop = function () {
      var snap = this.options.snap;

      if (!snap.loop || !this.pages || !this.pages.length) {
        return;
      }

      if (this.pages.length > 1) {
        snap._loopX = true;
      }
      if (this.pages[0] && this.pages[0].length > 1) {
        snap._loopY = true;
      }
      if (snap._loopX && snap._loopY) {
        warn('Loop does not support two direction at the same time.');
      }
    };

    BScroll.prototype._nearestSnap = function (x, y) {
      if (!this.pages.length) {
        return { x: 0, y: 0, pageX: 0, pageY: 0 };
      }

      var i = 0;
      // Check if we exceeded the snap threshold
      if (Math.abs(x - this.absStartX) <= this.snapThresholdX && Math.abs(y - this.absStartY) <= this.snapThresholdY) {
        return this.currentPage;
      }

      if (x > this.minScrollX) {
        x = this.minScrollX;
      } else if (x < this.maxScrollX) {
        x = this.maxScrollX;
      }

      if (y > this.minScrollY) {
        y = this.minScrollY;
      } else if (y < this.maxScrollY) {
        y = this.maxScrollY;
      }

      var l = this.pages.length;
      for (; i < l; i++) {
        if (x >= this.pages[i][0].cx) {
          x = this.pages[i][0].x;
          break;
        }
      }

      l = this.pages[i].length;

      var m = 0;
      for (; m < l; m++) {
        if (y >= this.pages[0][m].cy) {
          y = this.pages[0][m].y;
          break;
        }
      }

      if (i === this.currentPage.pageX) {
        i += this.directionX;

        if (i < 0) {
          i = 0;
        } else if (i >= this.pages.length) {
          i = this.pages.length - 1;
        }

        x = this.pages[i][0].x;
      }

      if (m === this.currentPage.pageY) {
        m += this.directionY;

        if (m < 0) {
          m = 0;
        } else if (m >= this.pages[0].length) {
          m = this.pages[0].length - 1;
        }

        y = this.pages[0][m].y;
      }

      return {
        x: x,
        y: y,
        pageX: i,
        pageY: m
      };
    };

    BScroll.prototype._goToPage = function (x) {
      var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
      var time = arguments[2];
      var easing = arguments[3];

      var snap = this.options.snap;

      if (!snap || !this.pages || !this.pages.length) {
        return;
      }

      easing = easing || snap.easing || ease.bounce;

      if (x >= this.pages.length) {
        x = this.pages.length - 1;
      } else if (x < 0) {
        x = 0;
      }

      if (!this.pages[x]) {
        return;
      }

      if (y >= this.pages[x].length) {
        y = this.pages[x].length - 1;
      } else if (y < 0) {
        y = 0;
      }

      var posX = this.pages[x][y].x;
      var posY = this.pages[x][y].y;

      time = time === undefined ? snap.speed || Math.max(Math.max(Math.min(Math.abs(posX - this.x), 1000), Math.min(Math.abs(posY - this.y), 1000)), 300) : time;

      this.currentPage = {
        x: posX,
        y: posY,
        pageX: x,
        pageY: y
      };
      this.scrollTo(posX, posY, time, easing);
    };

    BScroll.prototype.goToPage = function (x, y, time, easing) {
      var snap = this.options.snap;
      if (!snap || !this.pages || !this.pages.length) {
        return;
      }

      if (snap.loop) {
        var len = void 0;
        if (snap._loopX) {
          len = this.pages.length - 2;
          if (x >= len) {
            x = len - 1;
          } else if (x < 0) {
            x = 0;
          }
          x += 1;
        } else {
          len = this.pages[0].length - 2;
          if (y >= len) {
            y = len - 1;
          } else if (y < 0) {
            y = 0;
          }
          y += 1;
        }
      }
      this._goToPage(x, y, time, easing);
    };

    BScroll.prototype.next = function (time, easing) {
      var snap = this.options.snap;
      if (!snap) {
        return;
      }

      var x = this.currentPage.pageX;
      var y = this.currentPage.pageY;

      x++;
      if (x >= this.pages.length && this.hasVerticalScroll) {
        x = 0;
        y++;
      }

      this._goToPage(x, y, time, easing);
    };

    BScroll.prototype.prev = function (time, easing) {
      var snap = this.options.snap;
      if (!snap) {
        return;
      }

      var x = this.currentPage.pageX;
      var y = this.currentPage.pageY;

      x--;
      if (x < 0 && this.hasVerticalScroll) {
        x = 0;
        y--;
      }

      this._goToPage(x, y, time, easing);
    };

    BScroll.prototype.getCurrentPage = function () {
      var snap = this.options.snap;
      if (!snap) {
        return null;
      }

      if (snap.loop) {
        var currentPage = void 0;
        if (snap._loopX) {
          currentPage = extend({}, this.currentPage, {
            pageX: this.currentPage.pageX - 1
          });
        } else {
          currentPage = extend({}, this.currentPage, {
            pageY: this.currentPage.pageY - 1
          });
        }
        return currentPage;
      }
      return this.currentPage;
    };
  }

  function wheelMixin(BScroll) {
    BScroll.prototype.wheelTo = function () {
      var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;

      if (this.options.wheel) {
        var y = -index * this.itemHeight;
        this.scrollTo(0, y);
      }
    };

    BScroll.prototype.getSelectedIndex = function () {
      return this.options.wheel && this.selectedIndex;
    };

    BScroll.prototype._initWheel = function () {
      var wheel = this.options.wheel;
      if (!wheel.wheelWrapperClass) {
        wheel.wheelWrapperClass = 'wheel-scroll';
      }
      if (!wheel.wheelItemClass) {
        wheel.wheelItemClass = 'wheel-item';
      }
      if (!wheel.wheelDisabledItemClass) {
        wheel.wheelDisabledItemClass = 'wheel-disabled-item';
      }
      if (wheel.selectedIndex === undefined) {
        wheel.selectedIndex = 0;
      }
    };

    BScroll.prototype._findNearestValidWheel = function (y) {
      y = y > 0 ? 0 : y < this.maxScrollY ? this.maxScrollY : y;
      var wheel = this.options.wheel;
      var currentIndex = Math.abs(Math.round(-y / this.itemHeight));
      var cacheIndex = currentIndex;
      var items = this.items;
      // Impersonation web native select
      // first, check whether there is a enable item whose index is smaller than currentIndex
      // then, check whether there is a enable item whose index is bigger than currentIndex
      // otherwise, there are all disabled items, just keep currentIndex unchange
      while (currentIndex >= 0) {
        if (items[currentIndex].className.indexOf(wheel.wheelDisabledItemClass) === -1) {
          break;
        }
        currentIndex--;
      }

      if (currentIndex < 0) {
        currentIndex = cacheIndex;
        while (currentIndex <= items.length - 1) {
          if (items[currentIndex].className.indexOf(wheel.wheelDisabledItemClass) === -1) {
            break;
          }
          currentIndex++;
        }
      }

      // keep it unchange when all the items are disabled
      if (currentIndex === items.length) {
        currentIndex = cacheIndex;
      }
      // when all the items are disabled, this.selectedIndex should always be -1
      return {
        index: this.wheelItemsAllDisabled ? -1 : currentIndex,
        y: -currentIndex * this.itemHeight
      };
    };

    BScroll.prototype._checkWheelAllDisabled = function () {
      var wheel = this.options.wheel;
      var items = this.items;
      this.wheelItemsAllDisabled = true;
      for (var i = 0; i < items.length; i++) {
        if (items[i].className.indexOf(wheel.wheelDisabledItemClass) === -1) {
          this.wheelItemsAllDisabled = false;
          break;
        }
      }
    };
  }

  var INDICATOR_MIN_LEN = 8;

  function scrollbarMixin(BScroll) {
    BScroll.prototype._initScrollbar = function () {
      var _this = this;

      var _options$scrollbar = this.options.scrollbar,
        _options$scrollbar$fa = _options$scrollbar.fade,
        fade = _options$scrollbar$fa === undefined ? true : _options$scrollbar$fa,
        _options$scrollbar$in = _options$scrollbar.interactive,
        interactive = _options$scrollbar$in === undefined ? false : _options$scrollbar$in;

      this.indicators = [];
      var indicator = void 0;

      if (this.options.scrollX) {
        indicator = {
          el: createScrollbar('horizontal'),
          direction: 'horizontal',
          fade: fade,
          interactive: interactive
        };
        this._insertScrollBar(indicator.el);

        this.indicators.push(new Indicator(this, indicator));
      }

      if (this.options.scrollY) {
        indicator = {
          el: createScrollbar('vertical'),
          direction: 'vertical',
          fade: fade,
          interactive: interactive
        };
        this._insertScrollBar(indicator.el);
        this.indicators.push(new Indicator(this, indicator));
      }

      this.on('refresh', function () {
        for (var i = 0; i < _this.indicators.length; i++) {
          _this.indicators[i].refresh();
        }
      });

      if (fade) {
        this.on('scrollEnd', function () {
          for (var i = 0; i < _this.indicators.length; i++) {
            _this.indicators[i].fade();
          }
        });

        this.on('scrollCancel', function () {
          for (var i = 0; i < _this.indicators.length; i++) {
            _this.indicators[i].fade();
          }
        });

        this.on('scrollStart', function () {
          for (var i = 0; i < _this.indicators.length; i++) {
            _this.indicators[i].fade(true);
          }
        });

        this.on('beforeScrollStart', function () {
          for (var i = 0; i < _this.indicators.length; i++) {
            _this.indicators[i].fade(true, true);
          }
        });
      }

      this.on('destroy', function () {
        _this._removeScrollBars();
      });
    };

    BScroll.prototype._insertScrollBar = function (scrollbar) {
      this.wrapper.appendChild(scrollbar);
    };

    BScroll.prototype._removeScrollBars = function () {
      for (var i = 0; i < this.indicators.length; i++) {
        this.indicators[i].destroy();
      }
    };
  }

  function createScrollbar(direction) {
    var scrollbar = document.createElement('div');
    var indicator = document.createElement('div');

    scrollbar.style.cssText = 'position:absolute;z-index:9999;pointerEvents:none';
    indicator.style.cssText = 'box-sizing:border-box;position:absolute;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);border-radius:3px;';

    indicator.className = 'bscroll-indicator';

    if (direction === 'horizontal') {
      scrollbar.style.cssText += ';height:7px;left:2px;right:2px;bottom:0';
      indicator.style.height = '100%';
      scrollbar.className = 'bscroll-horizontal-scrollbar';
    } else {
      scrollbar.style.cssText += ';width:7px;bottom:2px;top:2px;right:1px';
      indicator.style.width = '100%';
      scrollbar.className = 'bscroll-vertical-scrollbar';
    }

    scrollbar.style.cssText += ';overflow:hidden';
    scrollbar.appendChild(indicator);

    return scrollbar;
  }

  function Indicator(scroller, options) {
    this.wrapper = options.el;
    this.wrapperStyle = this.wrapper.style;
    this.indicator = this.wrapper.children[0];
    this.indicatorStyle = this.indicator.style;
    this.scroller = scroller;
    this.direction = options.direction;
    if (options.fade) {
      this.visible = 0;
      this.wrapperStyle.opacity = '0';
    } else {
      this.visible = 1;
    }

    this.sizeRatioX = 1;
    this.sizeRatioY = 1;
    this.maxPosX = 0;
    this.maxPosY = 0;
    this.x = 0;
    this.y = 0;

    if (options.interactive) {
      this._addDOMEvents();
    }
  }

  Indicator.prototype.handleEvent = function (e) {
    switch (e.type) {
      case 'touchstart':
      case 'mousedown':
        this._start(e);
        break;
      case 'touchmove':
      case 'mousemove':
        this._move(e);
        break;
      case 'touchend':
      case 'mouseup':
      case 'touchcancel':
      case 'mousecancel':
        this._end(e);
        break;
    }
  };

  Indicator.prototype.refresh = function () {
    if (this._shouldShow()) {
      this.transitionTime();
      this._calculate();
      this.updatePosition();
    }
  };

  Indicator.prototype.fade = function (visible, hold) {
    var _this2 = this;

    if (hold && !this.visible) {
      return;
    }

    var time = visible ? 250 : 500;

    visible = visible ? '1' : '0';

    this.wrapperStyle[style.transitionDuration] = time + 'ms';

    clearTimeout(this.fadeTimeout);
    this.fadeTimeout = setTimeout(function () {
      _this2.wrapperStyle.opacity = visible;
      _this2.visible = +visible;
    }, 0);
  };

  Indicator.prototype.updatePosition = function () {
    if (this.direction === 'vertical') {
      var y = Math.round(this.sizeRatioY * this.scroller.y);

      if (y < 0) {
        this.transitionTime(500);
        var height = Math.max(this.indicatorHeight + y * 3, INDICATOR_MIN_LEN);
        this.indicatorStyle.height = height + 'px';
        y = 0;
      } else if (y > this.maxPosY) {
        this.transitionTime(500);
        var _height = Math.max(this.indicatorHeight - (y - this.maxPosY) * 3, INDICATOR_MIN_LEN);
        this.indicatorStyle.height = _height + 'px';
        y = this.maxPosY + this.indicatorHeight - _height;
      } else {
        this.indicatorStyle.height = this.indicatorHeight + 'px';
      }
      this.y = y;

      if (this.scroller.options.useTransform) {
        this.indicatorStyle[style.transform] = 'translateY(' + y + 'px)' + this.scroller.translateZ;
      } else {
        this.indicatorStyle.top = y + 'px';
      }
    } else {
      var x = Math.round(this.sizeRatioX * this.scroller.x);

      if (x < 0) {
        this.transitionTime(500);
        var width = Math.max(this.indicatorWidth + x * 3, INDICATOR_MIN_LEN);
        this.indicatorStyle.width = width + 'px';
        x = 0;
      } else if (x > this.maxPosX) {
        this.transitionTime(500);
        var _width = Math.max(this.indicatorWidth - (x - this.maxPosX) * 3, INDICATOR_MIN_LEN);
        this.indicatorStyle.width = _width + 'px';
        x = this.maxPosX + this.indicatorWidth - _width;
      } else {
        this.indicatorStyle.width = this.indicatorWidth + 'px';
      }

      this.x = x;

      if (this.scroller.options.useTransform) {
        this.indicatorStyle[style.transform] = 'translateX(' + x + 'px)' + this.scroller.translateZ;
      } else {
        this.indicatorStyle.left = x + 'px';
      }
    }
  };

  Indicator.prototype.transitionTime = function () {
    var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;

    this.indicatorStyle[style.transitionDuration] = time + 'ms';
  };

  Indicator.prototype.transitionTimingFunction = function (easing) {
    this.indicatorStyle[style.transitionTimingFunction] = easing;
  };

  Indicator.prototype.destroy = function () {
    this._removeDOMEvents();
    this.wrapper.parentNode.removeChild(this.wrapper);
  };

  Indicator.prototype._start = function (e) {
    var point = e.touches ? e.touches[0] : e;

    e.preventDefault();
    e.stopPropagation();

    this.transitionTime();

    this.initiated = true;
    this.moved = false;
    this.lastPointX = point.pageX;
    this.lastPointY = point.pageY;

    this.startTime = getNow();

    this._handleMoveEvents(addEvent);
    this.scroller.trigger('beforeScrollStart');
  };

  Indicator.prototype._move = function (e) {
    var point = e.touches ? e.touches[0] : e;

    e.preventDefault();
    e.stopPropagation();

    if (!this.moved) {
      this.scroller.trigger('scrollStart');
    }

    this.moved = true;

    var deltaX = point.pageX - this.lastPointX;
    this.lastPointX = point.pageX;

    var deltaY = point.pageY - this.lastPointY;
    this.lastPointY = point.pageY;

    var newX = this.x + deltaX;
    var newY = this.y + deltaY;

    this._pos(newX, newY);
  };

  Indicator.prototype._end = function (e) {
    if (!this.initiated) {
      return;
    }
    this.initiated = false;

    e.preventDefault();
    e.stopPropagation();

    this._handleMoveEvents(removeEvent);

    var snapOption = this.scroller.options.snap;
    if (snapOption) {
      var speed = snapOption.speed,
        _snapOption$easing = snapOption.easing,
        easing = _snapOption$easing === undefined ? ease.bounce : _snapOption$easing;

      var snap = this.scroller._nearestSnap(this.scroller.x, this.scroller.y);

      var time = speed || Math.max(Math.max(Math.min(Math.abs(this.scroller.x - snap.x), 1000), Math.min(Math.abs(this.scroller.y - snap.y), 1000)), 300);

      if (this.scroller.x !== snap.x || this.scroller.y !== snap.y) {
        this.scroller.directionX = 0;
        this.scroller.directionY = 0;
        this.scroller.currentPage = snap;
        this.scroller.scrollTo(snap.x, snap.y, time, easing);
      }
    }

    if (this.moved) {
      this.scroller.trigger('scrollEnd', {
        x: this.scroller.x,
        y: this.scroller.y
      });
    }
  };

  Indicator.prototype._pos = function (x, y) {
    if (x < 0) {
      x = 0;
    } else if (x > this.maxPosX) {
      x = this.maxPosX;
    }

    if (y < 0) {
      y = 0;
    } else if (y > this.maxPosY) {
      y = this.maxPosY;
    }

    x = Math.round(x / this.sizeRatioX);
    y = Math.round(y / this.sizeRatioY);

    this.scroller.scrollTo(x, y);
    this.scroller.trigger('scroll', {
      x: this.scroller.x,
      y: this.scroller.y
    });
  };

  Indicator.prototype._shouldShow = function () {
    if (this.direction === 'vertical' && this.scroller.hasVerticalScroll || this.direction === 'horizontal' && this.scroller.hasHorizontalScroll) {
      this.wrapper.style.display = '';
      return true;
    }
    this.wrapper.style.display = 'none';
    return false;
  };

  Indicator.prototype._calculate = function () {
    if (this.direction === 'vertical') {
      var wrapperHeight = this.wrapper.clientHeight;
      this.indicatorHeight = Math.max(Math.round(wrapperHeight * wrapperHeight / (this.scroller.scrollerHeight || wrapperHeight || 1)), INDICATOR_MIN_LEN);
      this.indicatorStyle.height = this.indicatorHeight + 'px';

      this.maxPosY = wrapperHeight - this.indicatorHeight;

      this.sizeRatioY = this.maxPosY / this.scroller.maxScrollY;
    } else {
      var wrapperWidth = this.wrapper.clientWidth;
      this.indicatorWidth = Math.max(Math.round(wrapperWidth * wrapperWidth / (this.scroller.scrollerWidth || wrapperWidth || 1)), INDICATOR_MIN_LEN);
      this.indicatorStyle.width = this.indicatorWidth + 'px';

      this.maxPosX = wrapperWidth - this.indicatorWidth;

      this.sizeRatioX = this.maxPosX / this.scroller.maxScrollX;
    }
  };

  Indicator.prototype._addDOMEvents = function () {
    var eventOperation = addEvent;
    this._handleDOMEvents(eventOperation);
  };

  Indicator.prototype._removeDOMEvents = function () {
    var eventOperation = removeEvent;
    this._handleDOMEvents(eventOperation);
    this._handleMoveEvents(eventOperation);
  };

  Indicator.prototype._handleMoveEvents = function (eventOperation) {
    if (!this.scroller.options.disableTouch) {
      eventOperation(window, 'touchmove', this);
    }
    if (!this.scroller.options.disableMouse) {
      eventOperation(window, 'mousemove', this);
    }
  };

  Indicator.prototype._handleDOMEvents = function (eventOperation) {
    if (!this.scroller.options.disableTouch) {
      eventOperation(this.indicator, 'touchstart', this);
      eventOperation(window, 'touchend', this);
    }
    if (!this.scroller.options.disableMouse) {
      eventOperation(this.indicator, 'mousedown', this);
      eventOperation(window, 'mouseup', this);
    }
  };

  function pullDownMixin(BScroll) {
    BScroll.prototype._initPullDown = function () {
      // must watch scroll in real time
      this.options.probeType = PROBE_REALTIME;
    };

    BScroll.prototype._checkPullDown = function () {
      var _options$pullDownRefr = this.options.pullDownRefresh,
        _options$pullDownRefr2 = _options$pullDownRefr.threshold,
        threshold = _options$pullDownRefr2 === undefined ? 90 : _options$pullDownRefr2,
        _options$pullDownRefr3 = _options$pullDownRefr.stop,
        stop = _options$pullDownRefr3 === undefined ? 40 : _options$pullDownRefr3;

      // check if a real pull down action

      if (this.directionY !== DIRECTION_DOWN || this.y < threshold) {
        return false;
      }

      if (!this.pulling) {
        this.pulling = true;
        this.trigger('pullingDown');
      }
      this.scrollTo(this.x, stop, this.options.bounceTime, ease.bounce);

      return this.pulling;
    };

    BScroll.prototype.finishPullDown = function () {
      this.pulling = false;
      this.resetPosition(this.options.bounceTime, ease.bounce);
    };

    BScroll.prototype.openPullDown = function () {
      var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;

      this.options.pullDownRefresh = config;
      this._initPullDown();
    };

    BScroll.prototype.closePullDown = function () {
      this.options.pullDownRefresh = false;
    };

    BScroll.prototype.autoPullDownRefresh = function () {
      var _options$pullDownRefr4 = this.options.pullDownRefresh,
        _options$pullDownRefr5 = _options$pullDownRefr4.threshold,
        threshold = _options$pullDownRefr5 === undefined ? 90 : _options$pullDownRefr5,
        _options$pullDownRefr6 = _options$pullDownRefr4.stop,
        stop = _options$pullDownRefr6 === undefined ? 40 : _options$pullDownRefr6;


      if (this.pulling) {
        return;
      }
      this.pulling = true;

      this.scrollTo(this.x, threshold);
      this.trigger('pullingDown');
      this.scrollTo(this.x, stop, this.options.bounceTime, ease.bounce);
    };
  }

  function pullUpMixin(BScroll) {
    BScroll.prototype._initPullUp = function () {
      // must watch scroll in real time
      this.options.probeType = PROBE_REALTIME;

      this.pullupWatching = false;
      this._watchPullUp();
    };

    BScroll.prototype._watchPullUp = function () {
      if (this.pullupWatching) {
        return;
      }
      this.pullupWatching = true;
      this.on('scroll', this._checkToEnd);
    };

    BScroll.prototype._checkToEnd = function (pos) {
      var _this = this;

      var _options$pullUpLoad$t = this.options.pullUpLoad.threshold,
        threshold = _options$pullUpLoad$t === undefined ? 0 : _options$pullUpLoad$t;

      if (this.movingDirectionY === DIRECTION_UP && pos.y <= this.maxScrollY + threshold) {
        // reset pullupWatching status after scroll end.
        this.once('scrollEnd', function () {
          _this.pullupWatching = false;
        });
        this.trigger('pullingUp');
        this.off('scroll', this._checkToEnd);
      }
    };

    BScroll.prototype.finishPullUp = function () {
      var _this2 = this;

      if (this.pullupWatching) {
        this.once('scrollEnd', function () {
          _this2._watchPullUp();
        });
      } else {
        this._watchPullUp();
      }
    };

    BScroll.prototype.openPullUp = function () {
      var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;

      this.options.pullUpLoad = config;
      this._initPullUp();
    };

    BScroll.prototype.closePullUp = function () {
      this.options.pullUpLoad = false;
      if (!this.pullupWatching) {
        return;
      }
      this.pullupWatching = false;
      this.off('scroll', this._checkToEnd);
    };
  }

  function mouseWheelMixin(BScroll) {
    BScroll.prototype._initMouseWheel = function () {
      var _this = this;

      this._handleMouseWheelEvent(addEvent);

      this.on('destroy', function () {
        clearTimeout(_this.mouseWheelTimer);
        clearTimeout(_this.mouseWheelEndTimer);
        _this._handleMouseWheelEvent(removeEvent);
      });

      this.firstWheelOpreation = true;
    };

    BScroll.prototype._handleMouseWheelEvent = function (eventOperation) {
      eventOperation(this.wrapper, 'wheel', this);
      eventOperation(this.wrapper, 'mousewheel', this);
      eventOperation(this.wrapper, 'DOMMouseScroll', this);
    };

    BScroll.prototype._onMouseWheel = function (e) {
      var _this2 = this;

      if (!this.enabled) {
        return;
      }
      e.preventDefault();

      if (this.options.stopPropagation) {
        e.stopPropagation();
      }

      if (this.firstWheelOpreation) {
        this.trigger('scrollStart');
      }
      this.firstWheelOpreation = false;

      var _options$mouseWheel = this.options.mouseWheel,
        _options$mouseWheel$s = _options$mouseWheel.speed,
        speed = _options$mouseWheel$s === undefined ? 20 : _options$mouseWheel$s,
        _options$mouseWheel$i = _options$mouseWheel.invert,
        invert = _options$mouseWheel$i === undefined ? false : _options$mouseWheel$i,
        _options$mouseWheel$e = _options$mouseWheel.easeTime,
        easeTime = _options$mouseWheel$e === undefined ? 300 : _options$mouseWheel$e;


      clearTimeout(this.mouseWheelTimer);
      this.mouseWheelTimer = setTimeout(function () {
        if (!_this2.options.snap && !easeTime) {
          _this2.trigger('scrollEnd', {
            x: _this2.x,
            y: _this2.y
          });
        }
        _this2.firstWheelOpreation = true;
      }, 400);

      var wheelDeltaX = void 0;
      var wheelDeltaY = void 0;

      switch (true) {
        case 'deltaX' in e:
          if (e.deltaMode === 1) {
            wheelDeltaX = -e.deltaX * speed;
            wheelDeltaY = -e.deltaY * speed;
          } else {
            wheelDeltaX = -e.deltaX;
            wheelDeltaY = -e.deltaY;
          }
          break;
        case 'wheelDeltaX' in e:
          wheelDeltaX = e.wheelDeltaX / 120 * speed;
          wheelDeltaY = e.wheelDeltaY / 120 * speed;
          break;
        case 'wheelDelta' in e:
          wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * speed;
          break;
        case 'detail' in e:
          wheelDeltaX = wheelDeltaY = -e.detail / 3 * speed;
          break;
        default:
          return;
      }

      var direction = invert ? -1 : 1;
      wheelDeltaX *= direction;
      wheelDeltaY *= direction;

      if (!this.hasVerticalScroll) {
        wheelDeltaX = wheelDeltaY;
        wheelDeltaY = 0;
      }

      var newX = void 0;
      var newY = void 0;
      if (this.options.snap) {
        newX = this.currentPage.pageX;
        newY = this.currentPage.pageY;

        if (wheelDeltaX > 0) {
          newX--;
        } else if (wheelDeltaX < 0) {
          newX++;
        }

        if (wheelDeltaY > 0) {
          newY--;
        } else if (wheelDeltaY < 0) {
          newY++;
        }

        this._goToPage(newX, newY);
        return;
      }

      newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0);
      newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0);

      this.movingDirectionX = this.directionX = wheelDeltaX > 0 ? -1 : wheelDeltaX < 0 ? 1 : 0;
      this.movingDirectionY = this.directionY = wheelDeltaY > 0 ? -1 : wheelDeltaY < 0 ? 1 : 0;

      if (newX > this.minScrollX) {
        newX = this.minScrollX;
      } else if (newX < this.maxScrollX) {
        newX = this.maxScrollX;
      }

      if (newY > this.minScrollY) {
        newY = this.minScrollY;
      } else if (newY < this.maxScrollY) {
        newY = this.maxScrollY;
      }

      var needTriggerEnd = this.y === newY;
      this.scrollTo(newX, newY, easeTime, ease.swipe);
      this.trigger('scroll', {
        x: this.x,
        y: this.y
      });
      clearTimeout(this.mouseWheelEndTimer);
      if (needTriggerEnd) {
        this.mouseWheelEndTimer = setTimeout(function () {
          _this2.trigger('scrollEnd', {
            x: _this2.x,
            y: _this2.y
          });
        }, easeTime);
      }
    };
  }

  function zoomMixin(BScroll) {
    BScroll.prototype._initZoom = function () {
      var _options$zoom = this.options.zoom,
        _options$zoom$start = _options$zoom.start,
        start = _options$zoom$start === undefined ? 1 : _options$zoom$start,
        _options$zoom$min = _options$zoom.min,
        min = _options$zoom$min === undefined ? 1 : _options$zoom$min,
        _options$zoom$max = _options$zoom.max,
        max = _options$zoom$max === undefined ? 4 : _options$zoom$max;

      this.scale = Math.min(Math.max(start, min), max);
      this.setScale(this.scale);
      this.scrollerStyle[style.transformOrigin] = '0 0';
    };

    BScroll.prototype._zoomTo = function (scale, originX, originY, startScale) {
      this.scaled = true;

      var lastScale = scale / (startScale || this.scale);
      this.setScale(scale);

      this.refresh();

      var newX = Math.round(this.startX - (originX - this.relativeX) * (lastScale - 1));
      var newY = Math.round(this.startY - (originY - this.relativeY) * (lastScale - 1));

      if (newX > this.minScrollX) {
        newX = this.minScrollX;
      } else if (newX < this.maxScrollX) {
        newX = this.maxScrollX;
      }

      if (newY > this.minScrollY) {
        newY = this.minScrollY;
      } else if (newY < this.maxScrollY) {
        newY = this.maxScrollY;
      }

      if (this.x !== newX || this.y !== newY) {
        this.scrollTo(newX, newY, this.options.bounceTime);
      }

      this.scaled = false;
    };

    BScroll.prototype.zoomTo = function (scale, x, y) {
      var _offsetToBody = offsetToBody(this.wrapper),
        left = _offsetToBody.left,
        top = _offsetToBody.top;

      var originX = x + left - this.x;
      var originY = y + top - this.y;
      this._zoomTo(scale, originX, originY);
    };

    BScroll.prototype._zoomStart = function (e) {
      var firstFinger = e.touches[0];
      var secondFinger = e.touches[1];
      var deltaX = Math.abs(firstFinger.pageX - secondFinger.pageX);
      var deltaY = Math.abs(firstFinger.pageY - secondFinger.pageY);

      this.startDistance = getDistance(deltaX, deltaY);
      this.startScale = this.scale;

      var _offsetToBody2 = offsetToBody(this.wrapper),
        left = _offsetToBody2.left,
        top = _offsetToBody2.top;

      this.originX = Math.abs(firstFinger.pageX + secondFinger.pageX) / 2 + left - this.x;
      this.originY = Math.abs(firstFinger.pageY + secondFinger.pageY) / 2 + top - this.y;

      this.trigger('zoomStart');
    };

    BScroll.prototype._zoom = function (e) {
      if (!this.enabled || this.destroyed || eventType[e.type] !== this.initiated) {
        return;
      }

      if (this.options.preventDefault) {
        e.preventDefault();
      }

      if (this.options.stopPropagation) {
        e.stopPropagation();
      }

      var firstFinger = e.touches[0];
      var secondFinger = e.touches[1];
      var deltaX = Math.abs(firstFinger.pageX - secondFinger.pageX);
      var deltaY = Math.abs(firstFinger.pageY - secondFinger.pageY);
      var distance = getDistance(deltaX, deltaY);
      var scale = distance / this.startDistance * this.startScale;

      this.scaled = true;

      var _options$zoom2 = this.options.zoom,
        _options$zoom2$min = _options$zoom2.min,
        min = _options$zoom2$min === undefined ? 1 : _options$zoom2$min,
        _options$zoom2$max = _options$zoom2.max,
        max = _options$zoom2$max === undefined ? 4 : _options$zoom2$max;


      if (scale < min) {
        scale = 0.5 * min * Math.pow(2.0, scale / min);
      } else if (scale > max) {
        scale = 2.0 * max * Math.pow(0.5, max / scale);
      }

      var lastScale = scale / this.startScale;

      var x = this.startX - (this.originX - this.relativeX) * (lastScale - 1);
      var y = this.startY - (this.originY - this.relativeY) * (lastScale - 1);

      this.setScale(scale);

      this.scrollTo(x, y, 0);
    };

    BScroll.prototype._zoomEnd = function (e) {
      if (!this.enabled || this.destroyed || eventType[e.type] !== this.initiated) {
        return;
      }

      if (this.options.preventDefault) {
        e.preventDefault();
      }

      if (this.options.stopPropagation) {
        e.stopPropagation();
      }

      this.isInTransition = false;
      this.isAnimating = false;
      this.initiated = 0;

      var _options$zoom3 = this.options.zoom,
        _options$zoom3$min = _options$zoom3.min,
        min = _options$zoom3$min === undefined ? 1 : _options$zoom3$min,
        _options$zoom3$max = _options$zoom3.max,
        max = _options$zoom3$max === undefined ? 4 : _options$zoom3$max;


      var scale = this.scale > max ? max : this.scale < min ? min : this.scale;

      this._zoomTo(scale, this.originX, this.originY, this.startScale);

      this.trigger('zoomEnd');
    };
  }

  // import { ease } from '../util/ease'

  // Number of items to instantiate beyond current view in the scroll direction.
  var RUNWAY_ITEMS = 30;

  // Number of items to instantiate beyond current view in the opposite direction.
  var RUNWAY_ITEMS_OPPOSITE = 10;

  // The animation interval (in ms) for fading in content from tombstones.
  var ANIMATION_DURATION_MS = 200;

  // The number of pixels of default additional length to allow scrolling to.
  var DEFAULT_SCROLL_RUNWAY = 2000;

  function infiniteMixin(BScroll) {
    BScroll.prototype._initInfinite = function () {
      this.options.probeType = 3;
      this.maxScrollY = -DEFAULT_SCROLL_RUNWAY;
      this.infiniteScroller = new InfiniteScroller(this, this.options.infinity);
    };
  }

  function isTombstoneNode(node) {
    if (node && node.classList) {
      return node.classList.contains('tombstone');
    }
  }

  function InfiniteScroller(scroller, options) {
    var _this = this;

    this.options = options;
    assert(typeof this.options.createTombstone === 'function', 'Infinite scroll need createTombstone Function to create tombstone');

    assert(typeof this.options.fetch === 'function', 'Infinite scroll need fetch Function to fetch new data.');

    assert(typeof this.options.render === 'function', 'Infinite scroll need render Function to render each item.');

    this.firstAttachedItem = 0;
    this.lastAttachedItem = 0;

    this.anchorScrollTop = 0;
    this.anchorItem = {
      index: 0,
      offset: 0
    };
    this.tombstoneHeight = 0;
    this.tombstoneWidth = 0;
    this.tombstones = [];
    this.tombstonesAnimationHandlers = [];

    this.items = [];
    this.loadedItems = 0;
    this.requestInProgress = false;
    this.hasMore = true;

    this.scroller = scroller;
    this.wrapperEl = this.scroller.wrapper;
    this.scrollerEl = this.scroller.scroller;

    this.scroller.on('resize', function () {
      _this.onResize();
    });
    this.scroller.on('destroy', function () {
      _this.destroy();
    });

    // wait scroll core init
    this._onResizeHandler = setTimeout(function () {
      _this.onResize();

      // must wait tombstoneHeight has size
      _this.scroller.on('scroll', function () {
        _this.onScroll();
      });
    });
  }

  InfiniteScroller.prototype.destroy = function () {
    var _this2 = this;

    // In extreme scene, destroy is triggered before _onResizeHandler
    clearTimeout(this._onResizeHandler);
    this.tombstonesAnimationHandlers.forEach(function (handler) {
      clearTimeout(handler);
    });
    this.tombstonesAnimationHandlers = null;
    this.items.forEach(function (item) {
      if (item.node) {
        _this2.scrollerEl.removeChild(item.node);
        item.node = null;
      }
    });
    this.scroller.infiniteScroller = null;
    this.scroller = null;
    this.wrapperEl = null;
    this.scrollerEl = null;
    this.items = null;
    this.tombstones = null;
  };

  InfiniteScroller.prototype.onScroll = function () {
    var scrollTop = -this.scroller.y;
    var delta = scrollTop - this.anchorScrollTop;
    if (scrollTop === 0) {
      this.anchorItem = {
        index: 0,
        offset: 0
      };
    } else {
      this.anchorItem = this._calculateAnchoredItem(this.anchorItem, delta);
    }

    this.anchorScrollTop = scrollTop;
    var lastScreenItem = this._calculateAnchoredItem(this.anchorItem, this.scroller.wrapperHeight);

    var start = this.anchorItem.index;
    var end = lastScreenItem.index;
    if (delta < 0) {
      start -= RUNWAY_ITEMS;
      end += RUNWAY_ITEMS_OPPOSITE;
    } else {
      start -= RUNWAY_ITEMS_OPPOSITE;
      end += RUNWAY_ITEMS;
    }
    this.fill(start, end);
    this.maybeRequestContent();
  };

  InfiniteScroller.prototype.onResize = function () {
    var tombstone = this.options.createTombstone();
    tombstone.style.position = 'absolute';
    this.scrollerEl.appendChild(tombstone);
    tombstone.style.display = '';
    this.tombstoneHeight = tombstone.offsetHeight;
    this.tombstoneWidth = tombstone.offsetWidth;
    this.scrollerEl.removeChild(tombstone);

    for (var i = 0; i < this.items.length; i++) {
      this.items[i].height = this.items[i].width = 0;
    }

    this.onScroll();
  };

  InfiniteScroller.prototype.fill = function (start, end) {
    this.firstAttachedItem = Math.max(0, start);
    if (!this.hasMore) {
      end = Math.min(end, this.items.length);
    }
    this.lastAttachedItem = end;
    this.attachContent();
  };

  InfiniteScroller.prototype.maybeRequestContent = function () {
    var _this3 = this;

    if (this.requestInProgress || !this.hasMore) {
      return;
    }
    var itemsNeeded = this.lastAttachedItem - this.loadedItems;
    if (itemsNeeded <= 0) {
      return;
    }
    this.requestInProgress = true;
    this.options.fetch(itemsNeeded).then(function (items) {
      _this3.requestInProgress = false;
      if (items) {
        _this3.addContent(items);
      } else {
        _this3.hasMore = false;
        var tombstoneLen = _this3._removeTombstones();
        var curPos = 0;
        if (_this3.anchorItem.index <= _this3.items.length) {
          curPos = _this3._fixScrollPosition();
          _this3._setupAnimations({}, curPos);
          _this3.scroller.resetPosition(_this3.scroller.options.bounceTime);
        } else {
          _this3.anchorItem.index -= tombstoneLen;
          curPos = _this3._fixScrollPosition();
          _this3._setupAnimations({}, curPos);
          _this3.scroller.stop();
          _this3.scroller.resetPosition();
          _this3.onScroll();
        }
      }
    });
  };

  InfiniteScroller.prototype.addContent = function (items) {
    for (var i = 0; i < items.length; i++) {
      if (this.items.length <= this.loadedItems) {
        this._addItem();
      }
      this.items[this.loadedItems++].data = items[i];
    }
    this.attachContent();
    this.maybeRequestContent();
  };

  InfiniteScroller.prototype.attachContent = function () {
    var unusedNodes = this._collectUnusedNodes();
    var tombstoneAnimations = this._createDOMNodes(unusedNodes);
    this._cleanupUnusedNodes(unusedNodes);
    this._cacheNodeSize();
    var curPos = this._fixScrollPosition();
    this._setupAnimations(tombstoneAnimations, curPos);
  };

  InfiniteScroller.prototype.resetMore = function () {
    this.hasMore = true;
  };

  InfiniteScroller.prototype._removeTombstones = function () {
    var markIndex = void 0;
    var tombstoneLen = 0;
    var itemLen = this.items.length;
    for (var i = 0; i < itemLen; i++) {
      var currentNode = this.items[i].node;
      var currentData = this.items[i].data;
      if ((!currentNode || isTombstoneNode(currentNode)) && !currentData) {
        // 0 should be excluded
        if (markIndex === void 0) {
          markIndex = i;
        }
        if (currentNode) {
          this.scrollerEl.removeChild(currentNode);
        }
      }
    }
    tombstoneLen = itemLen - markIndex;
    this.items.splice(markIndex);
    this.lastAttachedItem = Math.min(this.lastAttachedItem, this.items.length);
    return tombstoneLen;
  };

  InfiniteScroller.prototype._collectUnusedNodes = function () {
    var unusedNodes = [];
    for (var i = 0; i < this.items.length; i++) {
      // Skip the items which should be visible.
      if (i === this.firstAttachedItem) {
        i = this.lastAttachedItem - 1;
        continue;
      }
      var currentNode = this.items[i].node;
      if (currentNode) {
        if (isTombstoneNode(currentNode)) {
          // Cache tombstones for reuse
          this.tombstones.push(currentNode);
          this.tombstones[this.tombstones.length - 1].style.display = 'none';
        } else {
          unusedNodes.push(currentNode);
        }
      }
      this.items[i].node = null;
    }
    return unusedNodes;
  };

  InfiniteScroller.prototype._createDOMNodes = function (unusedNodes) {
    var tombstoneAnimations = {};
    for (var i = this.firstAttachedItem; i < this.lastAttachedItem; i++) {
      while (this.items.length <= i) {
        this._addItem();
      }
      var currentNode = this.items[i].node;
      var currentData = this.items[i].data;
      if (currentNode) {
        if (isTombstoneNode(currentNode) && currentData) {
          currentNode.style.zIndex = 1;
          tombstoneAnimations[i] = [currentNode, this.items[i].top - this.anchorScrollTop];
          this.items[i].node = null;
        } else {
          continue;
        }
      }
      var node = currentData ? this.options.render(currentData, unusedNodes.pop()) : this._getTombStone();
      node.style.position = 'absolute';
      this.items[i].top = -1;
      this.scrollerEl.appendChild(node);
      this.items[i].node = node;
    }
    return tombstoneAnimations;
  };

  InfiniteScroller.prototype._cleanupUnusedNodes = function (unusedNodes) {
    while (unusedNodes.length) {
      this.scrollerEl.removeChild(unusedNodes.pop());
    }
  };

  InfiniteScroller.prototype._cacheNodeSize = function () {
    for (var i = this.firstAttachedItem; i < this.lastAttachedItem; i++) {
      var item = this.items[i];
      // Only cache the height if we have the real contents, not a placeholder.
      if (item.data && !item.height) {
        var isTombstone = isTombstoneNode(item.node);
        item.height = isTombstone ? this.tombstoneHeight : item.node.offsetHeight;
        item.width = isTombstone ? this.tombstoneWidth : item.node.offsetWidth;
      }
    }
  };

  InfiniteScroller.prototype._fixScrollPosition = function () {
    this.anchorScrollTop = 0;
    for (var _i = 0; _i < this.anchorItem.index; _i++) {
      this.anchorScrollTop += this.items[_i].height || this.tombstoneHeight;
    }
    this.anchorScrollTop += this.anchorItem.offset;

    // Position all nodes.
    var curPos = this.anchorScrollTop - this.anchorItem.offset;
    var i = this.anchorItem.index;
    while (i > this.firstAttachedItem) {
      curPos -= this.items[i - 1].height || this.tombstoneHeight;
      i--;
    }

    return curPos;
  };

  InfiniteScroller.prototype._setupAnimations = function (tombstoneAnimations, curPos) {
    var _this4 = this;

    for (var i in tombstoneAnimations) {
      var animation = tombstoneAnimations[i];
      this.items[i].node.style[style.transform] = 'translateY(' + (this.anchorScrollTop + animation[1]) + 'px) scale(' + this.tombstoneWidth / this.items[i].width + ', ' + this.tombstoneHeight / this.items[i].height + ')';
      // Call offsetTop on the nodes to be animated to force them to apply current transforms.
      /* eslint-disable no-unused-expressions */
      this.items[i].node.offsetTop;
      animation[0].offsetTop;
      this.items[i].node.style[style.transition] = cssVendor + 'transform ' + ANIMATION_DURATION_MS + 'ms';
    }

    for (var _i2 = this.firstAttachedItem; _i2 < this.lastAttachedItem; _i2++) {
      var _animation = tombstoneAnimations[_i2];
      if (_animation) {
        var tombstoneNode = _animation[0];
        tombstoneNode.style[style.transition] = cssVendor + 'transform ' + ANIMATION_DURATION_MS + 'ms, opacity ' + ANIMATION_DURATION_MS + 'ms';
        tombstoneNode.style[style.transform] = 'translateY(' + curPos + 'px) scale(' + this.items[_i2].width / this.tombstoneWidth + ', ' + this.items[_i2].height / this.tombstoneHeight + ')';
        tombstoneNode.style.opacity = 0;
      }
      if (curPos !== this.items[_i2].top) {
        if (!_animation) {
          this.items[_i2].node.style[style.transition] = '';
        }
        this.items[_i2].node.style[style.transform] = 'translateY(' + curPos + 'px)';
      }
      this.items[_i2].top = curPos;
      curPos += this.items[_i2].height || this.tombstoneHeight;
    }

    this.scroller.maxScrollY = -(curPos - this.scroller.wrapperHeight + (this.hasMore ? DEFAULT_SCROLL_RUNWAY : 0));

    var tombstoneAnimationsHandler = setTimeout(function () {
      for (var _i3 in tombstoneAnimations) {
        var _animation2 = tombstoneAnimations[_i3];
        _animation2[0].style.display = 'none';
        // Tombstone can be recycled now.
        _this4.tombstones.push(_animation2[0]);
      }
    }, ANIMATION_DURATION_MS);

    this.tombstonesAnimationHandlers.push(tombstoneAnimationsHandler);
  };

  InfiniteScroller.prototype._getTombStone = function () {
    var tombstone = this.tombstones.pop();
    if (tombstone) {
      tombstone.style.display = '';
      tombstone.style.opacity = 1;
      tombstone.style[style.transform] = '';
      tombstone.style[style.transition] = '';
      return tombstone;
    }
    return this.options.createTombstone();
  };

  InfiniteScroller.prototype._addItem = function () {
    this.items.push({
      data: null,
      node: null,
      height: 0,
      width: 0,
      top: 0
    });
  };

  InfiniteScroller.prototype._calculateAnchoredItem = function (initialAnchor, delta) {
    if (delta === 0) {
      return initialAnchor;
    }
    var i = initialAnchor.index;
    var tombstones = 0;

    delta += initialAnchor.offset;
    if (delta < 0) {
      while (delta < 0 && i > 0 && this.items[i - 1].height) {
        delta += this.items[i - 1].height;
        i--;
      }
      tombstones = Math.max(-i, Math.ceil(Math.min(delta, 0) / this.tombstoneHeight));
    } else {
      while (delta > 0 && i < this.items.length && this.items[i].height && this.items[i].height < delta) {
        delta -= this.items[i].height;
        i++;
      }
      if (i >= this.items.length || !this.items[i].height) {
        tombstones = Math.floor(Math.max(delta, 0) / this.tombstoneHeight);
      }
    }
    i += tombstones;
    delta -= tombstones * this.tombstoneHeight;

    return {
      index: i,
      offset: delta
    };
  };

  function BScroll(el, options) {
    this.wrapper = typeof el === 'string' ? document.querySelector(el) : el;
    if (!this.wrapper) {
      warn('Can not resolve the wrapper DOM.');
    }
    this.scroller = this.wrapper.children[0];
    if (!this.scroller) {
      warn('The wrapper need at least one child element to be scroller.');
    }
    // cache style for better performance
    this.scrollerStyle = this.scroller.style;

    this._init(options);
  }

  initMixin(BScroll);
  coreMixin(BScroll);
  eventMixin(BScroll);
  snapMixin(BScroll);
  wheelMixin(BScroll);
  scrollbarMixin(BScroll);
  pullDownMixin(BScroll);
  pullUpMixin(BScroll);
  mouseWheelMixin(BScroll);
  zoomMixin(BScroll);
  infiniteMixin(BScroll);

  BScroll.Version = '1.15.0';

  return BScroll;

})));
